home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / System / JPartial Resources / JPartialResources.c < prev    next >
C/C++ Source or Header  |  1993-07-12  |  10KB  |  374 lines

  1. /*
  2.  * JPartialResources.c
  3.  *
  4.  * Jamie's partial resource calls that work under any system.
  5.  * Version 1.0.1
  6.  * © Copyright 1992-93 by Jamie R. McCarthy.  All rights reserved.
  7.  * This code can be both distributed and used freely.
  8.  * Internet: k044477@kzoo.edu            AppleLink: j.mccarthy
  9.  * Telephone:  800-421-4157 or US 616-665-7075 (9:00-5:00 Eastern time)
  10.  * I'm releasing this code with the hope that someone will get something
  11.  * out of it.  Feedback of any sort, even just letting me know that you're
  12.  * using it, is greatly appreciated!
  13.  *
  14.  * Many thanks to Marco Piovanelli (piovanel@dsi.unimi.it) for pointing
  15.  * out that my extra FSRead was unnecessary, and for showing me his
  16.  * source code.  One less disk access per call!
  17.  *
  18.  *
  19.  * "Be aware that having a copy of a resource in memory when you are using
  20.  *  the partial resource routines may cause problems.  If you have modified
  21.  *  the copy in memory and then access the resource on disk using either
  22.  *  the ReadPartialResource or WritePartialResource procedure, you will
  23.  *  lose changes made to the copy in memory."  - IM VI, 13-21
  24.  *
  25.  * By "copy in memory," this refers to having the entire resource loaded in.
  26.  * What it's saying is that ReadPartialResource and WritePartialResource
  27.  * consider themselves free to always purge (and maybe reload) the resource
  28.  * handle.  Incidentally, this code doesn't do that--it reads directly from
  29.  * disk--so if you're under system 6, your copy of the resource is safe.
  30.  * (Memory may be moved, however, so if the resource is purgeable, it _may_
  31.  * be purged.)
  32.  *
  33.  *
  34.  * "...there's really no reason to access resources directly."  - IM IV-16.
  35.  *
  36.  * Yeah right.
  37.  *
  38.  */
  39.  
  40.  
  41.  
  42. /******************************/
  43.  
  44. #include "JPartialResources.h"
  45.  
  46. /******************************/
  47.  
  48. #include <GestaltEqu.h>
  49. #include <Resources.h>
  50.  
  51. #include <Exceptions.h>
  52.  
  53. /******************************/
  54.  
  55. #if defined(__SYSEQU__)
  56. #define SetResErr(x) ((*(short*)ResErr) = (x))
  57. #elif defined(__LOMEM__)
  58. #define SetResErr(x) (ResErr = (x))
  59. #else
  60. #define SetResErr(x) ((*(short*)0x0A60) = (x))
  61. #endif
  62.  
  63. enum {
  64.         // See JPartialResources.h for comments.  If a future Errors.h
  65.         // file does indeed define these values, you can delete this
  66.         // enum and replace references to "kErrXxx" with "xxx".
  67.         // Or not.  It'll work fine the way it is.  It's up to you.
  68.     kErrInputOutOfBounds = -190,
  69.     kErrWritingPastEnd = -189
  70. } ;
  71.  
  72. /******************************/
  73.  
  74. #define kUninitializedFlag (42)
  75. Boolean partialResourceCallsAreAvailable = kUninitializedFlag;
  76.  
  77. /******************************/
  78.  
  79. typedef struct {
  80.     unsigned long        offsetToRsrcData;
  81.     unsigned long        offsetToRsrcMap;
  82.     unsigned long        lengthOfRsrcData;
  83.     unsigned long        lengthOfRsrcMap;
  84. } rsrcForkHeader;
  85.  
  86. typedef struct {
  87.     short                    rsrcID;
  88.     short                    offsetToRsrcName;
  89.     unsigned long        rsrcAttrs : 8;
  90.     unsigned long        offsetToRsrcData : 24;
  91.     Handle                rsrcHndl;
  92. } rsrcReference, *rsrcReferencePtr;
  93.  
  94. struct rsrcMap;
  95. typedef struct rsrcMap rsrcMap, *rsrcMapPtr, **rsrcMapHndl;
  96. struct rsrcMap {
  97.     rsrcForkHeader        copyOfRsrcForkHeader;
  98.     rsrcMapHndl            nextRsrcMapHndl;
  99.     short                    fileRefNum;
  100.     short                    fileAttrs;
  101.     unsigned short        offsetToTypeList;
  102.     unsigned short        offsetToNameList;
  103. } ;
  104.  
  105. /******************************/
  106.  
  107. void determineWhetherPRCallsAreAvailable(void);
  108.  
  109. /******************************/
  110.  
  111.     /*
  112.      * Just as _HomeResFile returns the refNum of a resource's file,
  113.      * so does getHomeRsrcMap() return a pointer to the resource
  114.      * file's map.  Warning--any calls that may move memory,
  115.      * including system memory, invalidate the pointer!
  116.      */
  117. rsrcMapPtr getHomeRsrcMap(Handle theResource);
  118.  
  119.     /*
  120.      * This uses RsrcMapEntry and getHomeRsrcMap() to find the
  121.      * offset from the beginning of a resource fork to the start
  122.      * of a resource's data.  The offset points to the data itself,
  123.      * which is just past the longword that tells its size.  If you
  124.      * happen to want the size too, use _SizeResource.
  125.      */
  126. long getRsrcDataOffsetIntoFile(Handle theResource);
  127.  
  128. long getRsrcFileMark(Handle theResource);
  129. void setRsrcFileMark(Handle theResource, long newMark);
  130.  
  131. /******************************/
  132.  
  133.  
  134.  
  135. pascal void jReadPartialResource(Handle theResource, long offset, void *buffer, long count)
  136. {
  137.     long rsrcSize;
  138.     
  139.     if (partialResourceCallsAreAvailable == kUninitializedFlag) {
  140.         determineWhetherPRCallsAreAvailable();
  141.     }
  142.     
  143.     rsrcSize = SizeResource(theResource);
  144.     if (ResError() != noErr || rsrcSize < 0) {
  145.         return ;
  146.     }
  147.     if (offset + count > rsrcSize) {
  148.         SetResErr(kErrInputOutOfBounds);
  149.         return ;
  150.     }
  151.     
  152.     if (partialResourceCallsAreAvailable) {
  153.         ReadPartialResource(theResource, offset, buffer, count);
  154.     } else {
  155.         
  156.         long theRsrcDataOffset;
  157.         long theRsrcDataLength;
  158.         OSErr theErr;
  159.         long oldMark;
  160.         
  161.         oldMark = getRsrcFileMark(theResource);
  162.         theRsrcDataOffset = getRsrcDataOffsetIntoFile(theResource);
  163.         if (theRsrcDataOffset < 0) {
  164.             SetResErr(kErrInputOutOfBounds);
  165.         } else {
  166.             setRsrcFileMark(theResource, theRsrcDataOffset + offset);
  167.             theErr = FSRead(HomeResFile(theResource), &count, buffer);
  168.             setRsrcFileMark(theResource, oldMark);
  169.         }
  170.         
  171.     }
  172. }
  173.  
  174.  
  175.  
  176. pascal void jWritePartialResource(Handle theResource, long offset, void *buffer, long count)
  177. {
  178.     long rsrcSize;
  179.     
  180.     if (partialResourceCallsAreAvailable == kUninitializedFlag) {
  181.         determineWhetherPRCallsAreAvailable();
  182.     }
  183.     
  184.         /*
  185.          * Disallow writing past the end of the resource;
  186.          * try to increase its size if possible.
  187.          */
  188.     rsrcSize = SizeResource(theResource);
  189.     if (ResError() != noErr || rsrcSize < 0) {
  190.         return ;
  191.     }
  192.     if (SizeResource(theResource) < offset+count) {
  193.         jSetResourceSize(theResource, offset+count);
  194.     }
  195.     
  196.         /*
  197.          * Check to be sure the size was successfully increased
  198.          * before continuing.
  199.          */
  200.     if (ResErr != noErr || SizeResource(theResource) < offset+count) {
  201.         return ;
  202.     }
  203.     
  204.     if (partialResourceCallsAreAvailable) {
  205.         WritePartialResource(theResource, offset, buffer, count);
  206.     } else {
  207.         
  208.         long theRsrcDataOffset;
  209.         OSErr theErr;
  210.         long oldMark;
  211.         
  212.         oldMark = getRsrcFileMark(theResource);
  213.         theRsrcDataOffset = getRsrcDataOffsetIntoFile(theResource);
  214.         if (theRsrcDataOffset < 0) return;
  215.         setRsrcFileMark(theResource, theRsrcDataOffset + offset);
  216.         theErr = FSWrite(HomeResFile(theResource), &count, buffer);
  217.         setRsrcFileMark(theResource, oldMark);
  218.         
  219.     }
  220. }
  221.  
  222.  
  223.  
  224. pascal void jSetResourceSize(Handle theResource, long size)
  225. {
  226.     if (partialResourceCallsAreAvailable == kUninitializedFlag) {
  227.         determineWhetherPRCallsAreAvailable();
  228.     }
  229.     
  230.     if (partialResourceCallsAreAvailable) {
  231.         SetResourceSize(theResource, size);
  232.     } else {
  233.             // Can't be done.  Changing the size of a resource on disk
  234.             // means moving everything in the fork that follows it,
  235.             // which means calculating and updating a lot of data.
  236.             // Doing something wrong would destroy the file, too, which
  237.             // is a potential disaster I'd rather not flirt with.
  238.         SetResErr(kErrWritingPastEnd);
  239.     }
  240. }
  241.  
  242.  
  243.  
  244. void determineWhetherPRCallsAreAvailable(void)
  245. {
  246.     if (partialResourceCallsAreAvailable == kUninitializedFlag) {
  247.         OSErr theOSErr;
  248.         long theResponse;
  249.         partialResourceCallsAreAvailable = FALSE;
  250. #define kAppleActuallyImplementedTheResourceMgrAttrSelectorInSystem7_0 (FALSE)
  251. #if kAppleActuallyImplementedTheResourceMgrAttrSelectorInSystem7_0
  252.         theOSErr = Gestalt(gestaltResourceMgrAttr, &theResponse);
  253.         if (theOSErr == noErr) {
  254.             if ( (theResponse & 1L<<gestaltPartialRsrcs) != 0 ) {
  255.                 partialResourceCallsAreAvailable = TRUE;
  256.             }
  257.         }
  258. #else
  259.         theOSErr = Gestalt(gestaltSystemVersion, &theResponse);
  260.         if (theOSErr == noErr) {
  261.             if ( (theResponse & 0x0000FFFF) < 0x0710 ) {
  262.                     // Pre-7.1, we can't trust gestaltResourceMgrAttr;
  263.                     // we have to read the system version.
  264.                 if ( (theResponse & 0x0000FFFF) >= 0x0700) {
  265.                     partialResourceCallsAreAvailable = TRUE;
  266.                 }
  267.             } else {
  268.                     // In 7.1 and beyond, we can trust gestaltResourceMgrAttr;
  269.                     // use it.
  270.                 theOSErr = Gestalt(gestaltResourceMgrAttr, &theResponse);
  271.                 if (theOSErr == noErr) {
  272.                     if ( (theResponse & 1L<<gestaltPartialRsrcs) != 0 ) {
  273.                         partialResourceCallsAreAvailable = TRUE;
  274.                     }
  275.                 }
  276.             }
  277.         }
  278. #endif
  279.     }
  280. }
  281.  
  282.  
  283.  
  284. /******************************/
  285.  
  286.  
  287.  
  288. rsrcMapPtr getHomeRsrcMap(Handle theResource)
  289. {
  290.     rsrcMapHndl cMapHndl;
  291.     short theRefNum;
  292.     
  293.     ASSERT(theResource != NULL);
  294.     
  295.     theRefNum = HomeResFile(theResource);
  296.     cMapHndl = (rsrcMapHndl) TopMapHndl;
  297.     
  298.     while ( (**cMapHndl).fileRefNum != theRefNum ) {
  299.         cMapHndl = (**cMapHndl).nextRsrcMapHndl;
  300.         if (cMapHndl == NULL) return NULL;
  301.     }
  302.     
  303.     return *cMapHndl;
  304. }
  305.  
  306.  
  307.  
  308. long getRsrcDataOffsetIntoFile(Handle theResource)
  309. {
  310.     rsrcReferencePtr theRsrcRefPtr;
  311.     long theOffsetToRsrcRef;
  312.     long theOffsetToRsrcData;
  313.     rsrcMapPtr theRsrcMapPtr;
  314.     
  315.     ASSERT(theResource != NULL);
  316.     
  317.     theOffsetToRsrcRef = RsrcMapEntry(theResource);
  318.     theRsrcMapPtr = getHomeRsrcMap(theResource);
  319.     if (theRsrcMapPtr == NULL) {
  320.         theOffsetToRsrcData = -1;
  321.     } else {
  322.         theRsrcRefPtr = (rsrcReferencePtr)
  323.             (((char*)theRsrcMapPtr) + theOffsetToRsrcRef);
  324.         
  325.         theOffsetToRsrcData = theRsrcRefPtr->offsetToRsrcData
  326.             + 0x0100                // add in the length of the resource header
  327.             + sizeof(long);    // skip over the longword that tells the resource's length
  328.     }
  329.     
  330.     return theOffsetToRsrcData;
  331. }
  332.  
  333.  
  334.  
  335. long getRsrcFileMark(Handle theResource)
  336. {
  337.     long theMark;
  338.     short theRefNum;
  339.     
  340.     ASSERT(theResource != NULL);
  341.     
  342.     theRefNum = HomeResFile(theResource);
  343.     if (ResError() != noErr) {
  344.         theMark = -1;
  345.     } else {
  346.         
  347.         OSErr theErr;
  348.         theErr = GetFPos(theRefNum, &theMark);
  349.         if (theErr != noErr) {
  350.             theMark = -1;
  351.         }
  352.         
  353.     }
  354.     
  355.     return theMark;
  356. }
  357.  
  358.  
  359.  
  360. void setRsrcFileMark(Handle theResource, long newMark)
  361. {
  362.     long theMark;
  363.     short theRefNum;
  364.     
  365.     ASSERT(theResource != NULL);
  366.     
  367.     theRefNum = HomeResFile(theResource);
  368.     if (ResError() != noErr) {
  369.         return ;
  370.     } else {
  371.         /* theOSErr = */ SetFPos(theRefNum, fsFromStart, newMark);
  372.     }
  373. }
  374.